Giter Site home page Giter Site logo

leonardocardoso / swiftlinkpreview Goto Github PK

View Code? Open in Web Editor NEW
1.4K 28.0 195.0 13.14 MB

It makes a preview from an URL, grabbing all the information such as title, relevant texts and images.

Home Page: https://leocardz.com/swift-link-preview-5a9860c7756f

License: MIT License

Ruby 1.95% Swift 93.37% HTML 2.98% Shell 1.15% C 0.56%
swift flow watchos tvos macos ios carthage cocoapods swift-package-manager crawler

swiftlinkpreview's Introduction

Swift Link Preview

Link Previewer for iOS, macOS, watchOS and tvOS

It makes a preview from an URL, grabbing all the information such as title, relevant texts and images.

Platform CocoaPods Carthage Compatible Swift Package Manager Build Status

Index


Visual Examples

UTF-8 Extended UTF-8 Gallery
UTF-8 Extended UTF-8 Gallery
Video Websites Images
Video Websites Images

Requirements and Details

  • iOS 8.0+ / macOS 10.11+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.0+
  • Built with Swift 3

Installation

CocoaPods

To use SwiftLinkPreview as a pod package just add the following in your Podfile file.

	source 'https://github.com/CocoaPods/Specs.git'
	platform :ios, '9.0'

	target 'Your Target Name' do
	  	use_frameworks!
	  	// ...
	  	pod 'SwiftLinkPreview', '~> 3.4.0'
	  	// ...
	end

Carthage

To use SwiftLinkPreview as a Carthage module package just add the following in your Cartfile file.

  	// ...
	github "LeonardoCardoso/SwiftLinkPreview" ~> 3.4.0
  	// ...

Swift Package Manager

To use SwiftLinkPreview as a Swift Package Manager package just add the following in your Package.swift file.

import PackageDescription

let package = Package(
  name: "Your Target Name",
  dependencies: [
  	// ...
    .Package(url: "https://github.com/LeonardoCardoso/SwiftLinkPreview.git", "3.4.0")
  	// ...
  ]
)

Manually

You just need to drop all contents in Sources folder, but .plist files, into Xcode project (make sure to enable "Copy items if needed" and "Create groups").

Usage

Instatiating

import SwiftLinkPreview

// ...

let slp = SwiftLinkPreview(session: URLSession.shared,
			   workQueue: SwiftLinkPreview.defaultWorkQueue,
			   responseQueue: DispatchQueue.main,
		           cache: DisabledCache.instance)

Requesting preview

let preview = slp.preview("Text containing URL",
                          onSuccess: { result in print("\(result)") },
                          onError: { error in print("\(error)")})
// preview.cancel() to cancel it.

result is a struct Response:

Response {
	let url: URL // URL
	let finalUrl: URL // unshortened URL
	let canonicalUrl: String // canonical URL
	let title: String // title
	let description: String // page description or relevant text
	let images: [String] // array of URLs of the images
	let image: String // main image
	let icon: String // favicon
	let video: String // video
	let price: String // price
}

Cancelling a request

let cancelablePreview = slp.preview(...,
				    onSuccess: ...,
				    onError: ...)

cancelablePreview.cancel()

Enabling and accessing cache

SLP has a built-in memory cache, so create your object as the following:

let slp = SwiftLinkPreview(cache: InMemoryCache())

To get the cached response:

if let cached = self.slp.cache.slp_getCachedResponse(url: String) {
    // Do whatever with the cached response
} else {
	// Perform preview otherwise
	slp.preview(...)
}

If you want to create your own cache, just implement this protocol and use it on the object initializer.

public protocol Cache {

    func slp_getCachedResponse(url: String) -> SwiftLinkPreview.Response?

    func slp_setCachedResponse(url: String, response: SwiftLinkPreview.Response?)
}

FLOW

flow

Important

You need to set Allow Arbitrary Loads to YES on your project's Info.plist file.

app security

If you don't want to use the option above and you are using SwiftLinkPreview for services of your knowledge, you can whitelist them on Info.plist file as well. You can read more about it here, here and here.

app security

Tips

Not all websites will have their info brought, you can treat the info that your implementation gets as you like. But here are two tips about posting a preview:

  • If some info is missing, you can offer the user to enter it. Take for example the description.
  • If more than one image is fetched, you can offer the user the feature of picking one image.

Tests

Feel free to fork this repo and add URLs on SwiftLinkPreviewTests/URLs.swift to test URLs and help improving this lib. The more URLs the better the reliability.

Information and Contact

Developed by @LeonardoCardoso.

Contact me either by Twitter @leocardz or emailing me to [email protected].

Related Projects

License

The MIT License (MIT)

Copyright (c) 2016 Leonardo Cardoso

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Follow me for the latest updates

swiftlinkpreview's People

Contributors

adamwulf avatar benlmyers avatar danielrhodes avatar fraserscottmorrison avatar jeffhodsdon avatar kkarayannis avatar leonardocardoso avatar lhunath avatar muhtasimtanmoy avatar nafis042 avatar ncirql avatar neobeppe avatar nova974 avatar onurgenes avatar rajeshbeats avatar saj4high2n avatar skunkworker avatar tuanquanghpvn avatar vinnyt avatar ypopovych avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftlinkpreview's Issues

Crash when parsing certain page

Hiya,

Ive encountered a crash which occurs when parsing this link http://go.acgedu.com/acg-parnellcollege/

Ive resolved it by putting a check that it can substring but this probably isn't addressing the root of the issue (maybe caused by the pages title being a url?)

    // Extract matches from string
    static func stringMatches(results: [NSTextCheckingResult], text: String, index: Int = 0) -> [String] {

        return results.map {
            let range = $0.rangeAtIndex(index)
            if text.characters.count > range.location + range.length {
                return (text as NSString).substringWithRange(range)
            }
            else {
                return ""
            }
        }
    }

infinite loop on www.youtube.com + issue with links that don't have http:// or https://

I was testing a couple URL's with the new updates you made and noticed that when SwiftLinkPreview parses www.youtube.com, the app freezes. Debugging reveals there seems to be some sort of infinite loop.

Also interestingly, when I tested with www.google.com i never get an image returned, same when I do google.com, but if I instead do even http://google.com (no www necessary) then an image is returned.

I've included a typical back trace for youtube issue (which I think is made more complicated because I'm using this swift code on an obj-c project, but anyways)

* thread #1: tid = 0x46efd, 0x0000000101907d04 libswiftCore.dylib`_swift_retain_(swift::HeapObject*) + 20, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
 * frame #0: 0x0000000101907d04 libswiftCore.dylib`_swift_retain_(swift::HeapObject*) + 20
    frame #1: 0x0000000101843d28 libswiftCore.dylib`generic specialization <Swift.String.UnicodeScalarView._ScratchGenerator with Swift.String.UnicodeScalarView._ScratchGenerator : Swift.GeneratorType in Swift> of Swift.UTF16.decode <A where A: Swift.GeneratorType, A.Element == Swift.UInt16> (inout A) -> Swift.UnicodeDecodingResult + 136
    frame #2: 0x0000000101877d7c libswiftCore.dylib`function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Dead> of static Swift.String.CharacterView.Index._measureExtendedGraphemeClusterForward (Swift.String.UnicodeScalarView.Index) -> Swift.Int + 140
    frame #3: 0x00000001018af718 libswiftCore.dylib`generic specialization <Swift.String.CharacterView.Index with Swift.String.CharacterView.Index : Swift.ForwardIndexType in Swift, Swift.Int with Swift.Int : Swift._SignedIntegerType in Swift, Swift.Int with Swift.Int : Swift._BuiltinIntegerLiteralConvertible in Swift, Swift._DisabledRangeIndex_> of (extension in Swift):Swift.ForwardIndexType.distanceTo (A) -> A.Distance + 136
    frame #4: 0x0000000101837d7c libswiftCore.dylib`protocol witness for Swift.ForwardIndexType.distanceTo (A) -> A.Distance in conformance Swift.String.CharacterView.Index : Swift.ForwardIndexType in Swift + 92
    frame #5: 0x000000010174f088 libswiftCore.dylib`(extension in Swift):Swift.CollectionType.count.getter : A.Index.Distance + 244
    frame #6: 0x0000000100201318 MyProject`static Regex.($0=0x0000000144d53960, index=2, text="<!DOCTYPE html><html lang=\"en\" data-cast-api-enabled=\"true\"><head><style name=\"www-roboto\">@font-face{font-family:\'Roboto\';font-style:italic;font-weight:500;src:local(\'Roboto Medium Italic\'),local(\'Roboto-MediumItalic\'),url(//fonts.gstatic.com/s/roboto/v15/OLffGBTaF0XFOW1gnuHF0Z0EAVxt0G0biEntp43Qt6E.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:italic;font-weight:400;src:local(\'Roboto Italic\'),local(\'Roboto-Italic\'),url(//fonts.gstatic.com/s/roboto/v15/W4wDsBUluyw0tK3tykhXEfesZW2xOQ-xsNqO47m55DA.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:400;src:local(\'Roboto Regular\'),local(\'Roboto-Regular\'),url(//fonts.gstatic.com/s/roboto/v15/zN7GBFwfMP4uA6AR0HCoLQ.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:500;src:local(\'Roboto Medium\'),local(\'Roboto-Medium\'),url(//fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexge8UUaCWcynf_cDxXwCLxiixG1c.ttf)format(\'truetype\');}</style><script name=\"www-roboto\">if (document.fonts &&"...) -> [String]).(closure #1) + 396 at Regex.swift:87
    frame #7: 0x0000000100201470 MyProject`thunk + 44 at Regex.swift:0
    frame #8: 0x0000000101749704 libswiftCore.dylib`(extension in Swift):Swift.CollectionType.map <A> ((A.Generator.Element) throws -> A1) throws -> Swift.Array<A1> + 716
    frame #9: 0x00000001001ffefc MyProject`static Regex.stringMatches(results=Swift.Array<Foundation.NSTextCheckingResult> @ 0x000000016fd993a8, text="<!DOCTYPE html><html lang=\"en\" data-cast-api-enabled=\"true\"><head><style name=\"www-roboto\">@font-face{font-family:\'Roboto\';font-style:italic;font-weight:500;src:local(\'Roboto Medium Italic\'),local(\'Roboto-MediumItalic\'),url(//fonts.gstatic.com/s/roboto/v15/OLffGBTaF0XFOW1gnuHF0Z0EAVxt0G0biEntp43Qt6E.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:italic;font-weight:400;src:local(\'Roboto Italic\'),local(\'Roboto-Italic\'),url(//fonts.gstatic.com/s/roboto/v15/W4wDsBUluyw0tK3tykhXEfesZW2xOQ-xsNqO47m55DA.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:400;src:local(\'Roboto Regular\'),local(\'Roboto-Regular\'),url(//fonts.gstatic.com/s/roboto/v15/zN7GBFwfMP4uA6AR0HCoLQ.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:500;src:local(\'Roboto Medium\'),local(\'Roboto-Medium\'),url(//fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexge8UUaCWcynf_cDxXwCLxiixG1c.ttf)format(\'truetype\');}</style><script name=\"www-roboto\">if (document.fonts &&"..., index=2, self=MyProject.Regex) -> [String] + 564 at Regex.swift:93
    frame #10: 0x0000000100200708 MyProject`static Regex.pregMatchAll(string="<!DOCTYPE html><html lang=\"en\" data-cast-api-enabled=\"true\"><head><style name=\"www-roboto\">@font-face{font-family:\'Roboto\';font-style:italic;font-weight:500;src:local(\'Roboto Medium Italic\'),local(\'Roboto-MediumItalic\'),url(//fonts.gstatic.com/s/roboto/v15/OLffGBTaF0XFOW1gnuHF0Z0EAVxt0G0biEntp43Qt6E.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:italic;font-weight:400;src:local(\'Roboto Italic\'),local(\'Roboto-Italic\'),url(//fonts.gstatic.com/s/roboto/v15/W4wDsBUluyw0tK3tykhXEfesZW2xOQ-xsNqO47m55DA.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:400;src:local(\'Roboto Regular\'),local(\'Roboto-Regular\'),url(//fonts.gstatic.com/s/roboto/v15/zN7GBFwfMP4uA6AR0HCoLQ.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:500;src:local(\'Roboto Medium\'),local(\'Roboto-Medium\'),url(//fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexge8UUaCWcynf_cDxXwCLxiixG1c.ttf)format(\'truetype\');}</style><script name=\"www-roboto\">if (document.fonts &&"..., regex="<span(.*?)>(.*?)</span>", index=2, self=MyProject.Regex) -> [String] + 1972 at Regex.swift:65
    frame #11: 0x000000010021aae0 MyProject`SwiftLinkPreview.(tag="span", content="<!DOCTYPE html><html lang=\"en\" data-cast-api-enabled=\"true\"><head><style name=\"www-roboto\">@font-face{font-family:\'Roboto\';font-style:italic;font-weight:500;src:local(\'Roboto Medium Italic\'),local(\'Roboto-MediumItalic\'),url(//fonts.gstatic.com/s/roboto/v15/OLffGBTaF0XFOW1gnuHF0Z0EAVxt0G0biEntp43Qt6E.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:italic;font-weight:400;src:local(\'Roboto Italic\'),local(\'Roboto-Italic\'),url(//fonts.gstatic.com/s/roboto/v15/W4wDsBUluyw0tK3tykhXEfesZW2xOQ-xsNqO47m55DA.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:400;src:local(\'Roboto Regular\'),local(\'Roboto-Regular\'),url(//fonts.gstatic.com/s/roboto/v15/zN7GBFwfMP4uA6AR0HCoLQ.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:500;src:local(\'Roboto Medium\'),local(\'Roboto-Medium\'),url(//fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexge8UUaCWcynf_cDxXwCLxiixG1c.ttf)format(\'truetype\');}</style><script name=\"www-roboto\">if (document.fonts &&"..., self=0x0000000144b67f40)(String, content : String) -> String + 316 at SwiftLinkPreview.swift:480
    frame #12: 0x000000010021a2e8 MyProject`SwiftLinkPreview.crawlCode(content="<!DOCTYPE html><html lang=\"en\" data-cast-api-enabled=\"true\"><head><style name=\"www-roboto\">@font-face{font-family:\'Roboto\';font-style:italic;font-weight:500;src:local(\'Roboto Medium Italic\'),local(\'Roboto-MediumItalic\'),url(//fonts.gstatic.com/s/roboto/v15/OLffGBTaF0XFOW1gnuHF0Z0EAVxt0G0biEntp43Qt6E.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:italic;font-weight:400;src:local(\'Roboto Italic\'),local(\'Roboto-Italic\'),url(//fonts.gstatic.com/s/roboto/v15/W4wDsBUluyw0tK3tykhXEfesZW2xOQ-xsNqO47m55DA.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:400;src:local(\'Roboto Regular\'),local(\'Roboto-Regular\'),url(//fonts.gstatic.com/s/roboto/v15/zN7GBFwfMP4uA6AR0HCoLQ.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:500;src:local(\'Roboto Medium\'),local(\'Roboto-Medium\'),url(//fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexge8UUaCWcynf_cDxXwCLxiixG1c.ttf)format(\'truetype\');}</style><script name=\"www-roboto\">if (document.fonts &&"..., self=0x0000000144b67f40) -> String + 140 at SwiftLinkPreview.swift:449
    frame #13: 0x000000010021b074 MyProject`@objc SwiftLinkPreview.crawlCode(String) -> String + 72 at SwiftLinkPreview.swift:0
    frame #14: 0x0000000100218994 MyProject`SwiftLinkPreview.crawlTitle(htmlCode="<!DOCTYPE html><html lang=\"en\" data-cast-api-enabled=\"true\"><head><style name=\"www-roboto\">@font-face{font-family:\'Roboto\';font-style:italic;font-weight:500;src:local(\'Roboto Medium Italic\'),local(\'Roboto-MediumItalic\'),url(//fonts.gstatic.com/s/roboto/v15/OLffGBTaF0XFOW1gnuHF0Z0EAVxt0G0biEntp43Qt6E.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:italic;font-weight:400;src:local(\'Roboto Italic\'),local(\'Roboto-Italic\'),url(//fonts.gstatic.com/s/roboto/v15/W4wDsBUluyw0tK3tykhXEfesZW2xOQ-xsNqO47m55DA.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:400;src:local(\'Roboto Regular\'),local(\'Roboto-Regular\'),url(//fonts.gstatic.com/s/roboto/v15/zN7GBFwfMP4uA6AR0HCoLQ.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:500;src:local(\'Roboto Medium\'),local(\'Roboto-Medium\'),url(//fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexge8UUaCWcynf_cDxXwCLxiixG1c.ttf)format(\'truetype\');}</style><script name=\"www-roboto\">if (document.fonts &&"..., self=0x0000000144b67f40) -> String + 912 at SwiftLinkPreview.swift:346
    frame #15: 0x0000000100218ce0 MyProject`@objc SwiftLinkPreview.crawlTitle(String) -> String + 72 at SwiftLinkPreview.swift:0
    frame #16: 0x000000010021533c MyProject`SwiftLinkPreview.(htmlCode="<!DOCTYPE html><html lang=\"en\" data-cast-api-enabled=\"true\"><head><style name=\"www-roboto\">@font-face{font-family:\'Roboto\';font-style:italic;font-weight:500;src:local(\'Roboto Medium Italic\'),local(\'Roboto-MediumItalic\'),url(//fonts.gstatic.com/s/roboto/v15/OLffGBTaF0XFOW1gnuHF0Z0EAVxt0G0biEntp43Qt6E.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:italic;font-weight:400;src:local(\'Roboto Italic\'),local(\'Roboto-Italic\'),url(//fonts.gstatic.com/s/roboto/v15/W4wDsBUluyw0tK3tykhXEfesZW2xOQ-xsNqO47m55DA.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:400;src:local(\'Roboto Regular\'),local(\'Roboto-Regular\'),url(//fonts.gstatic.com/s/roboto/v15/zN7GBFwfMP4uA6AR0HCoLQ.ttf)format(\'truetype\');}@font-face{font-family:\'Roboto\';font-style:normal;font-weight:500;src:local(\'Roboto Medium\'),local(\'Roboto-Medium\'),url(//fonts.gstatic.com/s/roboto/v15/RxZJdnzeo3R5zSexge8UUaCWcynf_cDxXwCLxiixG1c.ttf)format(\'truetype\');}</style><script name=\"www-roboto\">if (document.fonts &&"..., self=0x0000000144b67f40)(String) -> () + 196 at SwiftLinkPreview.swift:233
    frame #17: 0x00000001002134f8 MyProject`SwiftLinkPreview.(completion=0x000000010021b1fc MyProject`partial apply forwarder for MyProject.SwiftLinkPreview.(preview (Swift.ImplicitlyUnwrappedOptional<Swift.String>, onSuccess : (Swift.Dictionary<Swift.String, Swift.AnyObject>) -> (), onError : (MyProject.PreviewError) -> ()) -> ()).(closure #1).(closure #1) at SwiftLinkPreview.swift, onError=0x00000001002139bc MyProject`partial apply forwarder for reabstraction thunk helper from @callee_unowned @convention(block) (@unowned MyProject.PreviewError) -> (@unowned ()) to @callee_owned (@owned MyProject.PreviewError) -> (@unowned ()) at SwiftLinkPreview.swift, self=0x0000000144b67f40)(() -> (), onError : (PreviewError) -> ()) -> () + 1656 at SwiftLinkPreview.swift:179
    frame #18: 0x000000010021b950 MyProject`SwiftLinkPreview.(unshortened="www.youtube.com", self=0x0000000144b67f40, onSuccess=0x0000000100213938 MyProject`partial apply forwarder for reabstraction thunk helper from @callee_unowned @convention(block) (@unowned __ObjC.NSDictionary) -> (@unowned ()) to @callee_owned (@owned Swift.Dictionary<Swift.String, Swift.AnyObject>) -> (@unowned ()) at SwiftLinkPreview.swift, onError=0x00000001002139bc MyProject`partial apply forwarder for reabstraction thunk helper from @callee_unowned @convention(block) (@unowned MyProject.PreviewError) -> (@unowned ()) to @callee_owned (@owned MyProject.PreviewError) -> (@unowned ()) at SwiftLinkPreview.swift) -> (), onError : (PreviewError) -> ()) -> ()).(closure #1) + 500 at SwiftLinkPreview.swift:44
    frame #19: 0x000000010021ca20 MyProject`SwiftLinkPreview.(completion=0x0000000100212d8c MyProject`partial apply forwarder for MyProject.SwiftLinkPreview.(preview (Swift.ImplicitlyUnwrappedOptional<Swift.String>, onSuccess : (Swift.Dictionary<Swift.String, Swift.AnyObject>) -> (), onError : (MyProject.PreviewError) -> ()) -> ()).(closure #1) at SwiftLinkPreview.swift, url="www.youtube.com")(NSURL, completion : (NSURL) -> ()) -> ()).(closure #1).(closure #2) + 76 at SwiftLinkPreview.swift:146

and here it is again without all the HTML code it was processing

* thread #1: tid = 0x46efd, 0x0000000101907d04 libswiftCore.dylib`_swift_retain_(swift::HeapObject*) + 20, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
 * frame #0: 0x0000000101907d04 libswiftCore.dylib`_swift_retain_(swift::HeapObject*) + 20
    frame #1: 0x0000000101843d28 libswiftCore.dylib`generic specialization <Swift.String.UnicodeScalarView._ScratchGenerator with Swift.String.UnicodeScalarView._ScratchGenerator : Swift.GeneratorType in Swift> of Swift.UTF16.decode <A where A: Swift.GeneratorType, A.Element == Swift.UInt16> (inout A) -> Swift.UnicodeDecodingResult + 136
    frame #2: 0x0000000101877d7c libswiftCore.dylib`function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Dead> of static Swift.String.CharacterView.Index._measureExtendedGraphemeClusterForward (Swift.String.UnicodeScalarView.Index) -> Swift.Int + 140
    frame #3: 0x00000001018af718 libswiftCore.dylib`generic specialization <Swift.String.CharacterView.Index with Swift.String.CharacterView.Index : Swift.ForwardIndexType in Swift, Swift.Int with Swift.Int : Swift._SignedIntegerType in Swift, Swift.Int with Swift.Int : Swift._BuiltinIntegerLiteralConvertible in Swift, Swift._DisabledRangeIndex_> of (extension in Swift):Swift.ForwardIndexType.distanceTo (A) -> A.Distance + 136
    frame #4: 0x0000000101837d7c libswiftCore.dylib`protocol witness for Swift.ForwardIndexType.distanceTo (A) -> A.Distance in conformance Swift.String.CharacterView.Index : Swift.ForwardIndexType in Swift + 92
    frame #5: 0x000000010174f088 libswiftCore.dylib`(extension in Swift):Swift.CollectionType.count.getter : A.Index.Distance + 244
    frame #6: 0x0000000100201318 MyProject`static Regex.($0=0x0000000144d53960, index=2, text="..." -> [String]).(closure #1) + 396 at Regex.swift:87
    frame #7: 0x0000000100201470 MyProject`thunk + 44 at Regex.swift:0
    frame #8: 0x0000000101749704 libswiftCore.dylib`(extension in Swift):Swift.CollectionType.map <A> ((A.Generator.Element) throws -> A1) throws -> Swift.Array<A1> + 716
    frame #9: 0x00000001001ffefc MyProject`static Regex.stringMatches(results=Swift.Array<Foundation.NSTextCheckingResult> @ 0x000000016fd993a8, text="...", index=2, self=MyProject.Regex) -> [String] + 564 at Regex.swift:93
    frame #10: 0x0000000100200708 MyProject`static Regex.pregMatchAll(string="...", regex="<span(.*?)>(.*?)</span>", index=2, self=MyProject.Regex) -> [String] + 1972 at Regex.swift:65
    frame #11: 0x000000010021aae0 MyProject`SwiftLinkPreview.(tag="span", content="...", self=0x0000000144b67f40)(String, content : String) -> String + 316 at SwiftLinkPreview.swift:480
    frame #12: 0x000000010021a2e8 MyProject`SwiftLinkPreview.crawlCode(content="...", self=0x0000000144b67f40) -> String + 140 at SwiftLinkPreview.swift:449
    frame #13: 0x000000010021b074 MyProject`@objc SwiftLinkPreview.crawlCode(String) -> String + 72 at SwiftLinkPreview.swift:0
    frame #14: 0x0000000100218994 MyProject`SwiftLinkPreview.crawlTitle(htmlCode="...", self=0x0000000144b67f40) -> String + 912 at SwiftLinkPreview.swift:346
    frame #15: 0x0000000100218ce0 MyProject`@objc SwiftLinkPreview.crawlTitle(String) -> String + 72 at SwiftLinkPreview.swift:0
    frame #16: 0x000000010021533c MyProject`SwiftLinkPreview.(htmlCode="...", self=0x0000000144b67f40)(String) -> () + 196 at SwiftLinkPreview.swift:233
    frame #17: 0x00000001002134f8 MyProject`SwiftLinkPreview.(completion=0x000000010021b1fc MyProject`partial apply forwarder for MyProject.SwiftLinkPreview.(preview (Swift.ImplicitlyUnwrappedOptional<Swift.String>, onSuccess : (Swift.Dictionary<Swift.String, Swift.AnyObject>) -> (), onError : (MyProject.PreviewError) -> ()) -> ()).(closure #1).(closure #1) at SwiftLinkPreview.swift, onError=0x00000001002139bc MyProject`partial apply forwarder for reabstraction thunk helper from @callee_unowned @convention(block) (@unowned MyProject.PreviewError) -> (@unowned ()) to @callee_owned (@owned MyProject.PreviewError) -> (@unowned ()) at SwiftLinkPreview.swift, self=0x0000000144b67f40)(() -> (), onError : (PreviewError) -> ()) -> () + 1656 at SwiftLinkPreview.swift:179
    frame #18: 0x000000010021b950 MyProject`SwiftLinkPreview.(unshortened="www.youtube.com", self=0x0000000144b67f40, onSuccess=0x0000000100213938 MyProject`partial apply forwarder for reabstraction thunk helper from @callee_unowned @convention(block) (@unowned __ObjC.NSDictionary) -> (@unowned ()) to @callee_owned (@owned Swift.Dictionary<Swift.String, Swift.AnyObject>) -> (@unowned ()) at SwiftLinkPreview.swift, onError=0x00000001002139bc MyProject`partial apply forwarder for reabstraction thunk helper from @callee_unowned @convention(block) (@unowned MyProject.PreviewError) -> (@unowned ()) to @callee_owned (@owned MyProject.PreviewError) -> (@unowned ()) at SwiftLinkPreview.swift) -> (), onError : (PreviewError) -> ()) -> ()).(closure #1) + 500 at SwiftLinkPreview.swift:44
    frame #19: 0x000000010021ca20 MyProject`SwiftLinkPreview.(completion=0x0000000100212d8c MyProject`partial apply forwarder for MyProject.SwiftLinkPreview.(preview (Swift.ImplicitlyUnwrappedOptional<Swift.String>, onSuccess : (Swift.Dictionary<Swift.String, Swift.AnyObject>) -> (), onError : (MyProject.PreviewError) -> ()) -> ()).(closure #1) at SwiftLinkPreview.swift, url="www.youtube.com")(NSURL, completion : (NSURL) -> ()) -> ()).(closure #1).(closure #2) + 76 at SwiftLinkPreview.swift:146

Unable to parse

"https://goo.gl/maps/8cu9dtwedbn" is a shared link from google maps. In most of the cases we getting "This URL cannot be opened" and localised description as "The operation couldn’t be completed. (SwiftLinkPreview.PreviewError error 2.)". And if it successful then also we are unable to get image.

The file “Dev” couldn’t be opened because you don’t have permission to view it.

XCode version 7.2.1 and Mac 10.11.6
Development target: iOS 8.4
Swift 2.1

Error:
It was building but when installing the app on the device I was getting:

The file “Dev” couldn’t be opened because you don’t have permission to view it.

I solved the issue by doing the following:

  • Downloaded: https://github.com/LeonardoCardoso/SwiftLinkPreview/releases/tag/0.1.6

  • Added the unzipped folder to my Libraries in my project.

  • From the SwiftLinkPreview folder removed the all the plists except for the Info.plist, Example folder, package.swift

  • Removed derived data, clean the project

  • Add the binary to my project: General->Embedded Binaries

Not Safe in Application Extensions?

I am trying to build my App Extension with SwiftLinkPreview and I am getting this error:
warning: linking against a dylib which is not safe for use in application extensions: .../Carthage/Build/iOS/SwiftLinkPreview.framework/SwiftLinkPreview

Is it true that this library is not save in app extensions? if so, is there anything I can do to make it safe? Thanks.

Regex mismatches (or allowing for more types of urls)

Is this library only supposed support certain type of url formats?

When I tested it with www.google.com (or any other url without the http://) I got "No URL has been found"

Similarly, when I tried to use http://www.google.com I got "An error occurred when parsing the HTML"

It worked fine with the examples you used but I think requiring the http:// is a bit strict in that if a we are analyzing user typed messages, it might be quite common to see something like youtube.com which IS picked up by NSDataDetector as a link, but won't be valid as a link according to the regex you are using.

Caching Results (Feature)

Said it before, but again, great project.

Wanted to suggest a caching feature for web results. It seems like using this in some sort of feed or table view might be a common use case, so currently whenever that happens and cells are reused there are more network calls and work done in parsing the same exact content over and over.

It would be really cool and useful if it could cache results (maybe allow for customization on how many days) so that the same network requests wouldn't have to be made over and over again every time a view loads or a cell is reused.

2.0.2 adds ambiguous method error in Swift builds

My bad - the preview method wrapper I added for Objective-C compatibility conflicts with the Swift version, in Swift builds (not in Objective-C builds), causing the compiler to generate an error about ambiguous methods.

Solution is to provide an alternate name for the method. Will post pull request.

Objective-C

You don't happen to have an objective-c version of this, do you? It looks awesome.

Lower osx.deployment_target to 10.10

Is there a reason the OSX deployment target in the podspec is set to 10.11? I have an app which for now requires at least 10.10 and I can't install your pod because of that :/ If it doesn't affect your project, could you consider lowering that requirement?

There is no guide to build Example

Although its not an issue in the project but just if someone wants to run the Example then they need to initialize pod and following pods are required.

Pods for SwiftLinkPreviewExample

pod 'SwiftLinkPreview', '~> 2.0.4'
pod 'SwiftyDrop', '~>3.0'
pod 'ImageSlideshow'
pod 'Alamofire'
pod 'AlamofireImage'

end

Cocoapods still not working

it's me again haha.

Resolving dependencies of Podfile
[!] Unable to find a specification for SwiftLinkPreview

Cache response is not called on UITableView

  • Xcode 8.2
  • Swift 3
  • ios 9x - 10x

    Everytime I call swift link preview it always call and set the data, how can I get the cache response, I am loading the preview data on UITableview so what should I do to get response from cache data when cell is recycled
    `
    slp.preview(

                LinkObj.path,
                onSuccess: { result in
                //print("\(result)")
    
                cell.linkTitle.text = result[.title] as? String
                cell.linkURL.text = result[.canonicalUrl] as? String
                cell.linkDescription.text = result[.description] as? String
                
                let imageURL = result[.image] as? String
                print(imageURL!)
                cell.linkImage.kf.setImage(with: URL(string: imageURL!), placeholder: UIImage(named:""), options: [.transition(ImageTransition.fade(1))], progressBlock: nil, completionHandler: nil)
                
                cell.linkBtnRef.layer.setValue(result[.url], forKey: "linkURL")
                cell.linkBtnRef.addTarget(self, action: #selector(self.linkClickedOnLinkCell), for: .touchUpInside)
                
        },
            onError: { error in
                print("\(error)")
        }
        )`
    
  • Twitter return errors

    First nice lib, but a few problems.

    Twitter

    Error Domain=NSPOSIXErrorDomain Code=100 "Protocol error" UserInfo={NSErrorPeerAddressKey=<CFData 0x618000281270 [0x101e3bbc0]>{length = 16, capacity = 16, bytes = 0x100201bb68f42ac10000000000000000}, _kCFStreamErrorCodeKey=100, _kCFStreamErrorDomainKey=1}

    Linkedin fails as well. I don't know why yet.

    Make URL detection work for text containing new-lines

    Very simple fix here, too short for doing pull-request:

    Simply change line 207 in SwiftLinkPreview::extractURL(text)
    from:
    let pieces: [String] = text.components(separatedBy: " ").filter { $0.trim.isValidURL() }
    to:
    let pieces: [String] = text.components(separatedBy: .whitespacesAndNewlines).filter { $0.trim.isValidURL() }

    PS: Do you mind also making the extractURL(...) method public? It's really useful UX-wise to determine whether a URL WOULD be detected and do some logic against the previously-detected one and thus ONLY invoke a new preview(..) if the URL is different. This happens when typing in a text-field for example and you don't want to fire a new request off upon every keystroke. Instead only want when the detected URL has actually changed.

    Cheers!

    Crashes when "Open in Safari" is pressed

    Thread 1: signal SIGABRT

    -[SwiftLinkPreviewExample.ViewController openWithAction:]: unrecognized selector sent to instance 0x7f8ae1605790

    Environments:

    • Swift 4 / Xcode 9.1 / iOS 11

    Suggestion

    Hi,

    Little suggestion : Integration of "favicon.ico"

    Regards

    Carthage build fails

    *** Building scheme "SwiftLinkPreview" in SwiftLinkPreview.xcodeproj
    ** BUILD FAILED **
    
    
    The following build commands failed:
        CompileSwift normal arm64 /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Sources/SwiftLinkPreview.swift
        CompileSwift normal arm64 /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Sources/Regex.swift
        CompileSwift normal arm64 /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Sources/PreviewError.swift
        CompileSwift normal arm64 /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Package.swift
        CompileSwiftSources normal arm64 com.apple.xcode.tools.swift.compiler
    (5 failures)
    /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Package.swift:9:8: error: no such module 'PackageDescription'
    /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Package.swift:9:8: error: no such module 'PackageDescription'
    /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Package.swift:9:8: error: no such module 'PackageDescription'
    /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Package.swift:9:8: error: no such module 'PackageDescription'
    A shell task (/usr/bin/xcrun xcodebuild -project /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/SwiftLinkPreview.xcodeproj -scheme SwiftLinkPreview -configuration Release -sdk iphoneos ONLY_ACTIVE_ARCH=NO BITCODE_GENERATION_MODE=bitcode CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES clean build) failed with exit code 65:
    ** BUILD FAILED **
    
    
    The following build commands failed:
        CompileSwift normal arm64 /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Sources/SwiftLinkPreview.swift
        CompileSwift normal arm64 /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Sources/Regex.swift
        CompileSwift normal arm64 /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Sources/PreviewError.swift
        CompileSwift normal arm64 /Users/IO/Development/Other/other-chat-ios/Carthage/Checkouts/SwiftLinkPreview/Package.swift
        CompileSwiftSources normal arm64 com.apple.xcode.tools.swift.compiler
    (5 failures)
    
    

    iOS 8 compatibility

    By any chance could you lower the compatibility of the Swift 3 version to iOS 8?

    Make extractURL public

    Hi there could you please make the extractURL(...) method public?

    This is really useful UX-wise to determine whether a URL WOULD be detected and do some logic against the previously-detected one and thus ONLY invoke a new preview(..) if the URL is different.

    This happens when the user types in a text-field and you don't want to fire a new request upon every keystroke. Instead only want to do so when the detected URL has actually changed from the previous one. For example if they type a link in a comment text box and continue to type their message afterwards, the rest of the keystrokes will resolve to the same URL and thus you can skip the URL fetching.

    NOTE: The only change required for this is to change the 'internal' keyword to 'public' in the SwiftLinkPreview extension's extractURL(text:) method.

    Thx!

    CRASH when no URLs present.

    First off, thanks for a nice open-source repo.

    The Issue
    Line 122 in SwiftLinkPreview.swift results in a crash if the text in question contains no URLs. In line, 121 the 'pieces' array ends up being empty if there's no URL and then the first element is grabbed in the following line, resulting in a 'fatal error: Index out of range' exception and crash.

    Unfortunately, I'm short on time or I would make a PR with a robust solution, however, I might recommend checking the count of the array or using the 'first' property, which fails more gracefully than the subscript 'pieces[0]'

    Thanks again

    A few questions + Example issues

    Hello,
    Currently your example project seems to rely on 3 pods, but I was only able to find that out by looking at the readme file on that directory. It would be a lot easier if you provided a pod file, did basic integration with cocoa pods and then provided an .xcworkspace that people could use to run the sample project out the box.

    Also, i noticed you pushed a release removing the dependency on Alamofire, but the sample code still requires a pod that deals with alamofire and use it (for example: if let source: AlamofireSource = AlamofireSource(urlString: image) { on ViewController.swift:125).

    I've noticed that you have been very active recently on this project though so I'm just assuming it's still a work in progress. I am very interested in using this in a project I am working on but because you are in the process of making changes I wanted to ask you your opinions first.

    Seeing as you are in currently making changes, is it at a place where it would be ok to integrate now and update as you make changes, or are there things you are getting to or want to check out first? We are getting very close to testing the app for a production release, and want to get a sense if we should delay integration for a few days/a week until its a little more stable?

    P.S.: It's on obj-c project and some of my pods don't play nicely together with use_frameworks! so i'd have to manually integrate it, which is why I ask

    Create tests

    • Head
      • Twitter
      • Facebook
      • Meta
    • Body
      • Images
        • Single
        • Gallery
      • Text (Relevant Texts)
        • Title
        • Description

    Broken stuff?

    I'm lately trying to stay away from Pods. That's why I've wanted to download your project manually. You just need to drop SwiftLinkPreview folder into Xcode project (make sure to enable "Copy items if needed" and "Create groups"). There is no folder SwiftLinkPreview.

    Also the Example Project is kinda empty. No SwiftLinkPreview files and doesn't build at all. I thought about missing pod install, but a podfile is missing also.

    Init for version 2.0.0

    I have gotten the version 2.0.0 from carthage and now the line
    let slp = SwiftLinkPreview()
    crashes.

    Hangs when crawling for description

    The URL I'm crawling is: https://www.flickr.com/explore/

    I stepped through the source and see that it's hanging in crawlDescription() at the crawlCode() call.

    I see this in the debugger console: SendDelegateMessage(NSInvocation *): delegate (<CFNotificationCenter 0x6080000aed60 [0x108679df0]>) failed to return after waiting 10 seconds. main run loop mode: kCFRunLoopDefaultMode

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. 📊📈🎉

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google ❤️ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.