Skip to content

Commit

Permalink
fix(ics): adds proper detection for iOS Safari
Browse files Browse the repository at this point in the history
This will fix the issue of .ics files not downloading on iOS Safari.
  • Loading branch information
jshor committed Oct 24, 2020
1 parent 3f4b39f commit 754397c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 3 deletions.
45 changes: 43 additions & 2 deletions src/utils/__tests__/ics.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,36 @@ describe('IcsUtil', () => {
})

describe('on non-Safari browsers', () => {
it('should invoke FileSaver.saveAs()', () => {
it('should invoke FileSaver.saveAs() on iOS Chrome', () => {
Object.defineProperty(navigator, 'userAgent', {
value: 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1',
writable: true
})

ics.download('july 4.ics', 'foobar')

expect(FileSaver.saveAs).toHaveBeenCalledTimes(1)
expect(safariFileSave).not.toHaveBeenCalled()
})

it('should invoke FileSaver.saveAs() on iOS Firefox', () => {
Object.defineProperty(navigator, 'userAgent', {
value: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/11.0b9935 Mobile/15E216 Safari/605.1.15',
writable: true
})

ics.download('july 4.ics', 'foobar')

expect(FileSaver.saveAs).toHaveBeenCalledTimes(1)
expect(safariFileSave).not.toHaveBeenCalled()
})

it('should invoke FileSaver.saveAs() on non-iOS browsers', () => {
Object.defineProperty(navigator, 'userAgent', {
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43',
writable: true
})

ics.download('july 4.ics', 'foobar')

expect(FileSaver.saveAs).toHaveBeenCalledTimes(1)
Expand All @@ -139,7 +168,19 @@ describe('IcsUtil', () => {
})

describe('on Safari', () => {
it('should invoke safariFileSave()', () => {
it('should invoke safariFileSave() if the user agent contains `safari`', () => {
Object.defineProperty(navigator, 'userAgent', {
value: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A356 Safari/604.1',
writable: true
})

ics.download('july 4.ics', 'foobar')

expect(FileSaver.saveAs).not.toHaveBeenCalled()
expect(safariFileSave).toHaveBeenCalledTimes(1)
})

it('should invoke safariFileSave() if the `safari` property is present on `window`', () => {
Object.defineProperty(window, 'safari', {
value: {
window: true
Expand Down
17 changes: 16 additions & 1 deletion src/utils/ics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ const getRrule = (recurrence: IRecurrence): string => {
return data.toIcsParamString(rrule)
}

/**
* Returns true if the current browser is Safari.
*
* @returns {boolean}
*/
const isSafari = (): boolean => {
return window.hasOwnProperty('safari') || (
// check to ensure navigator is not Chrome (which includes Safari in the user agent)
/^((?!chrome|android).)*safari/i.test(navigator.userAgent) &&
// browsers on iOS are wrappers around Safari, but include CriOS (Chrome), FxiOS (Firefox), etc.
!/(cr|fx)ios[^a-z]/i.test(navigator.userAgent)
)
}

/**
* Downloads the given ics as an iCalendar file.
*
Expand All @@ -92,10 +106,11 @@ const getRrule = (recurrence: IRecurrence): string => {
const download = (title: string, data: string): void => {
const fileName = getFileName(title)

if (window.hasOwnProperty('safari')) {
if (isSafari()) {
safariFileSave(data, fileName)
} else {
const blob = getBlob(data)

FileSaver.saveAs(blob, fileName)
}
}
Expand Down

0 comments on commit 754397c

Please sign in to comment.